using System;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using Yoozoo.Gameplay.Managers;
using Yoozoo.Manager;
using Yoozoo.Modules.Update;
using ZString;

namespace Yoozoo.Modules.TraceEvent
{
    public class ApmReport : MonoBehaviour
    {
        
        private float sampleTimeInterval;
        private float minTimeInterval;
        private bool isSelfDestory;
        private string sceneName;
        private string sceneArg;
        private string loadingTime;
        private long _blockingFps;
        private long _shakeFps;
        private int _drallcall = 0;//每帧绘图次数
        private readonly float BlockTime = 0.1f;//卡顿
        private readonly float ShakeFrame = 8;//抖动
        private readonly float Frame18 = 18;//低于18帧数
        private readonly float Frame25 = 25;//低于25帧数
        private int _overFrame18Count;
        private int _overFrame25Count;
        private float _overFrame25Percent;
        
        public float OverFrame18Percent { get; private set; }
        public float AverageFps { get; private set; }
        
        //硬件参数
        private string _deviceId;//设备ID
        private string _deviceName;//设备名称
        private string _graphicsDeviceType;//显卡类型
        private string _graphicsDeviceVersion;//显卡版本号
        private int _graphicsMemorySize;//显存大小MB

        private float _timer;
        private float _testTime;
        private float _instantFps;
        private int _frames;
        private float _lastFps;
        private List<float> _fpsList = new List<float>(2048);
        private string _screenSize;
        private float _overTime = 60;
        private string performanceSvrPath;
        
        public List<float> LoadingTimeList = new List<float>();
        public ApmReport Instance;

        private void Awake()
        {
            Instance = this;
        }
        
        void Start()
        {
            ResetData();
            _deviceId = GetDeviceId();
            _deviceName = SystemInfo.deviceName;
            _graphicsDeviceType = SystemInfo.graphicsDeviceType.ToString();
            _graphicsDeviceVersion = SystemInfo.graphicsDeviceVersion;
            _graphicsMemorySize = SystemInfo.graphicsMemorySize;
            _screenSize = Screen.width + "*" + Screen.height;
        }
        
        /// <summary>
        /// 初始化参数，开始采集
        /// </summary>
        /// <param name="url">请求URL地址</param>
        /// <param name="sampleTimeInterval">自动采集间隔</param>
        /// <param name="minTimeInterval">自动采集最小间隔</param>
        /// <param name="isSelfDestory">是否自动销毁</param>
        /// <param name="sceneName">采集场景</param>
        /// <param name="sceneArg">采集场景参数</param>
        /// <param name="loadingTime">加载时长</param>
        public void InitParam(
            string url, 
            float sampleTimeInterval, 
            float minTimeInterval, 
            bool isSelfDestory,
            string sceneName = "", 
            string sceneArg = "", 
            string loadingTime = "")
        {
            performanceSvrPath = url;
            if (sampleTimeInterval == 0)
            {
                this.sampleTimeInterval = float.MaxValue;
            }
            else
            {
                this.sampleTimeInterval = sampleTimeInterval;
            }
            this.minTimeInterval = minTimeInterval;
            this.isSelfDestory = isSelfDestory;
            this.sceneName = sceneName;
            this.sceneArg = sceneArg;
            this.loadingTime = loadingTime;
        }
        
        /// <summary>
        /// 停止采集
        /// </summary>
        public void StopCollect()
        {
            Destroy(this.gameObject);
            ResetData();
        }

        /// <summary>
        /// 停止采集并上报数据
        /// </summary>
        public void StopCollectAndSendReport(bool isInterval = false)
        {
            if (isInterval)
            {
                //需要判定当前时长是否满足最低要求
                if (_timer > minTimeInterval) CalResult();
            }
            else
            {
                CalResult();
            }
            Destroy(this.gameObject);
            ResetData();
        }

        public bool IsOverMinIntervalTime()
        {
            return _timer > minTimeInterval;
        }

        /// <summary>
        /// 每帧计算
        /// </summary>
        void Update()
        {
            float deltaTime = Time.unscaledDeltaTime;
            _instantFps = 1.0f / deltaTime;
            if (Mathf.Abs(_lastFps - _instantFps) >= ShakeFrame)
            {
                _shakeFps++;
            }
            _lastFps = _instantFps;
            if (deltaTime >= BlockTime)
            {
                _blockingFps++;
            }
            if (_instantFps >= Frame18)
            {
                _overFrame18Count++;
            }
            if (_instantFps >= Frame25)
            {
                _overFrame25Count++;
            }
            _fpsList.Add(_instantFps);
            _timer += deltaTime;
            _frames++;
            
            //当总时间超过间隔时间时，自动重置
            if (_timer > sampleTimeInterval && !isSelfDestory)
            {
                CalResult();
                ResetData();
            }
        }
        
        /// <summary>
        /// 计算数据
        /// </summary>
        private void CalResult()
        {
            try
            {
                _testTime = _timer;
                AverageFps = _frames / _timer;
                OverFrame18Percent = _overFrame18Count / (float)_frames;
                _overFrame25Percent = _overFrame25Count / (float)_frames;
                _timer = 0;
                
                //FPS均值
                float sum = 0;
                foreach (var fps in _fpsList)
                {
                    sum += (fps - AverageFps) * (fps - AverageFps);
                }
                
                //场景平均加载时间
                float sum4 = 0;
                foreach (var loadingTime in LoadingTimeList)
                {
                    sum4 += loadingTime;
                }
                
                //发送数据
                SendData(
                    AverageFps, 
                    _blockingFps, 
                    _shakeFps, 
                    _deviceName, 
                    _graphicsDeviceType,
                    _graphicsDeviceVersion, 
                    _graphicsMemorySize);
                
                //自动销毁
                if (isSelfDestory)
                {
                    Destroy(this.gameObject);
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"CalResult failed !reason: {e.StackTrace}");
                throw;
            }
        }

        /// <summary>
        /// 重置数据
        /// </summary>
        private void ResetData()
        {
            AverageFps = 0;
            _blockingFps = 0;
            _shakeFps = 0;
            _timer = 0;
            _instantFps = 0;
            _overFrame18Count = 0;
            _overFrame25Count = 0;
            _frames = 0;
            _lastFps = 0;
            _fpsList.Clear();
        }

        /// <summary>
        /// 获取超过18帧的比率
        /// </summary>
        /// <returns></returns>
        public float GetOverFrame18Percent()
        {
            OverFrame18Percent = _overFrame18Count / (float)_frames;
            return OverFrame18Percent;
        }

        /// <summary>
        /// 获取平均帧率
        /// </summary>
        /// <returns></returns>
        public float GetAverageFps()
        {
            return _frames / _timer;
        }

        /// <summary>
        /// 获取设备ID
        /// </summary>
        /// <returns></returns>
        private string GetDeviceId()
        {
            string id = string.Empty;

            if (SDKManager.IsSDK())
                id = SDKManager.GetDeviceIDGroup();
            else
                id = DependencyInjection.DeviceAgent?.GetDeviceId() ?? "";
            
            return id;
        }

        /// <summary>
        /// 发送上报的数据
        /// </summary>
        /// <param name="averageFps"></param>
        /// <param name="blockingFps"></param>
        /// <param name="shakeFps"></param>
        /// <param name="deviceName"></param>
        /// <param name="graphicsDeviceType"></param>
        /// <param name="graphicsDeviceVersion"></param>
        /// <param name="graphicsMemorySize"></param>
        private void SendData(
            float averageFps,
            long blockingFps, 
            long shakeFps,
            string deviceName, 
            string graphicsDeviceType, 
            string graphicsDeviceVersion, 
            int graphicsMemorySize)
        {
            using (zstring.Block())
            {
                Debug.Log(zstring.Format("ClientRoleExtra ?? HitSystemInfo deviceId = {0}", _deviceId));
                zstring zStringBuilder = "{";
                zStringBuilder += zstring.Format("\"log_type\":\"{0}\",","客户端APM性能打点" );
                zStringBuilder += zstring.Format("\"extra_1\":\"{0}\",",  deviceName);
                zStringBuilder += zstring.Format("\"extra_2\":\"{0}\",",  SystemInfo.graphicsDeviceName);
                zStringBuilder += zstring.Format("\"extra_3\":\"{0}\",",   graphicsDeviceType);
                zStringBuilder += zstring.Format("\"extra_4\":\"{0}\",",   graphicsDeviceVersion);
                zStringBuilder += zstring.Format("\"extra_5\":\"{0}\",",   graphicsMemorySize.ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_6\":\"{0}\",",   _screenSize);
                zStringBuilder += zstring.Format("\"extra_7\":\"{0}\",",   (DeviceInfo.systemMemorySize - DeviceInfo.GetUseMemory()).ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_8\":\"{0}\",",   DeviceInfo.systemMemorySize.ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_9\":\"{0}\",",   DeviceInfo.GetFreeDiskSize().ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_10\":\"{0}\",",   DeviceInfo.GetAllDiskSize().ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_11\":\"{0}\",",   _testTime.ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_12\":\"{0}\",",    Mathf.RoundToInt(averageFps).ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_13\":\"{0}\",",   Mathf.RoundToInt(blockingFps).ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_14\":\"{0}\",",   Mathf.RoundToInt(shakeFps).ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_15\":\"{0}\",",   OverFrame18Percent.ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_16\":\"{0}\",",   _overFrame25Percent.ToString(CultureInfo.InvariantCulture));
                zStringBuilder += zstring.Format("\"extra_20\":\"{0}\",",   sceneName);
                zStringBuilder += zstring.Format("\"extra_21\":\"{0}\",",   sceneArg);
                zStringBuilder += zstring.Format("\"extra_22\":\"{0}\",",   loadingTime);
                zStringBuilder += zstring.Format("\"extra_23\":\"{0}\",",    DeviceInfo.GetBatteryRemain());
                zStringBuilder += zstring.Format("\"extra_24\":\"{0}\",",   DeviceInfo.GetBatteryTemperature());
                zStringBuilder += zstring.Format("\"extra_26\":\"{0}\"",   DependencyInjection.GetCurrentQuality());
                zStringBuilder += "}";
                
                EventTracingManager.GetInstance().ClientRoleExtra(zStringBuilder, true);
                ResetData();
            }
        }
    }
}